#include "config.h"

#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include <dirent.h>
#include <limits.h>
#include "pipes/pipes.h"

enum eAppType gAppType;
sObject* gStdin;
sObject* gStdout;
sObject* gStderr;

sObject* gGlobalPipe;

enum eLineField gLineField;
enum eKanjiCode gKanjiCode;

void (*pipes_job_done)(int job_num, char* job_title);

sObject* gJobs;

void run_init(enum eAppType app_type)
{
    gAppType = app_type;
    gStdin = FD_NEW_STACK();
    gStdout = FD_NEW_STACK();
    gStderr = FD_NEW_STACK();
    gJobs = VECTOR_NEW_GC(16, FALSE);
    hash_put(gPipesObject, "_jobs", gJobs);

    pipes_job_done = NULL;

    gLineField = kLF;
    gKanjiCode = kByte;

    gGlobalPipe = FD_NEW_STACK();
}

void run_final()
{
}

sObject* fd_new_from_stack()
{
    sObject* self = stack_get_free_object(T_FD);

    SFD(self).mBuf = MALLOC(1024);

    SFD(self).mBufSize = 1024;
    SFD(self).mBufLen = 0;

    *SFD(self).mBuf = 0;

    SFD(self).mLines = VECTOR_NEW_MALLOC(32);

    return self;
}

void fd_delete_stack(sObject* self)
{
    sObject* v = SFD(self).mLines;
    int i;
    for(i=0; i<vector_size(v); i++) {
        FREE(vector_item(v, i));
    }
    vector_delete_malloc(SFD(self).mLines);

    FREE(SFD(self).mBuf);
}

// TRUE: Success
// FALSE: signal interrupt. set RCODE_SIGNAL_INTERRUPT on rcode and err message
BOOL bufsiz_write(int fd, char* buf)
{
    int len = strlen(buf);
    char* p = buf;
    while(*p) {
        int size;
        if(len - (p - buf) < BUFSIZ) {
            size = len - (p-buf);
        }
        else {
            size = BUFSIZ;
        }

        if(write(fd, p, size) < 0) {
            if(errno == EINTR) {
                while(!gPipesSigInt);
                gPipesSigInt = FALSE;
                return FALSE;
            }
            return FALSE;
        }

        if(gPipesSigInt) {
            gPipesSigInt = FALSE;
            return FALSE;
        }

        p+=size;
    }

    return TRUE;
}

/*
// TRUE: Success
// FALSE: signal interrupt. set RCODE_SIGNAL_INTERRUPT on rcode and err message
BOOL fd_read(sObject* self)
{
}
*/

void fd_split(sObject* self, enum eLineField lf)
{
    sObject* v = SFD(self).mLines;

    if(SFD(self).mLinesLineField != lf) {
        int i;
        for(i=0; i<vector_size(v); i++) {
            FREE(vector_item(v, i));
        }
        vector_clear(v);
    }

    if(vector_size(v) == 0) {
        char* p = SFD(self).mBuf;
        char* new_line = p;
        if(lf == kCRLF) {
            while(1) {
                if(*p == '\r' && *(p+1) == '\n') {
                    p+=2;
                    const int size = p - new_line;
                    char* line = MALLOC(size + 1);
                    memcpy(line, new_line, size);
                    line[size] = 0;
                    vector_add(v, line);
                    new_line = p;
                }
                else if(*p == 0) {
                    const int size = p - new_line;
                    if(size > 0) {
                        char* line = MALLOC(size + 1 + 2);
                        memcpy(line, new_line, size);
                        line[size] = '\r';
                        line[size+1] = '\n';
                        line[size+2] = 0;
                        vector_add(v, line);
                    }
                    break;
                }
                else {
                    p++;
                }
            }
        }
        else {
            char split_char;
            if(lf == kLF) {
                split_char = '\n';
            }
            else if(lf == kCR) {
                split_char = '\r';
            }
            else {
                split_char = '\a';
            }

            while(1) {
                if(*p == split_char) {
                    const int size = p - new_line + 1;
                    char* line = MALLOC(size + 1);
                    memcpy(line, new_line, size);
                    line[size] = 0;
                    vector_add(v, line);
                    p++;
                    new_line = p;
                }
                else if(*p == 0) {
                    const int size = p - new_line;
                    if(size > 0) {
                        char* line = MALLOC(size + 1 + 1);
                        memcpy(line, new_line, size);
                        line[size] = split_char;
                        line[size+1] = 0;
                        vector_add(v, line);
                    }
                    break;
                }
                else {
                    p++;
                }
            }
        }

        SFD(self).mLinesLineField = lf;
    }
}


// TRUE: Success
// FALSE: signal interrupt
static BOOL memcpy_buf(char* dest, char* src, size_t size)
{
    char* p = dest;
    char* p2 = src;
    while(1) {
        if(gPipesSigInt) {
            gPipesSigInt = FALSE;
            return FALSE;
        }
        if(size - (p - dest) < BUFSIZ) {
            memcpy(p, p2, size - (p-dest));
            break;
        }
        else {
            memcpy(p, p2, BUFSIZ);
            p+=BUFSIZ;
            p2+=BUFSIZ;
        }
    }

    return TRUE;
}

void fd_clear(sObject* self)
{
    SFD(self).mBufLen = 0;
    SFD(self).mBuf[0] = 0;

    int i;
    for(i=0; i<vector_size(SFD(self).mLines); i++) {
        FREE(vector_item(SFD(self).mLines, i));
    }
    vector_clear(SFD(self).mLines);
}

// TRUE: Success
// FALSE: signal interrupt. set RCODE_SIGNAL_INTERRUPT on rcode and err message
BOOL fd_write(sObject* self, char* str)
{
    const int len = strlen(str);

    if((SFD(self).mBufLen+len) >= SFD(self).mBufSize) {
        int new_size = (SFD(self).mBufSize + len) * 1.8;

        SFD(self).mBuf = REALLOC(SFD(self).mBuf, new_size);

        SFD(self).mBufSize = new_size;
    }

    if(!memcpy_buf(SFD(self).mBuf + SFD(self).mBufLen, str, len + 1)) {
        return FALSE;
    }
    SFD(self).mBufLen += len;

    return TRUE;
}

BOOL fd_write3(sObject* self, char* str, int size)
{
    const int len = size;

    if((SFD(self).mBufLen+len) >= SFD(self).mBufSize) {
        int new_size = (SFD(self).mBufSize + len) * 1.8;

        SFD(self).mBuf = REALLOC(SFD(self).mBuf, new_size);

        SFD(self).mBufSize = new_size;
    }

    if(!memcpy_buf(SFD(self).mBuf + SFD(self).mBufLen, str, size)) {
        return FALSE;
    }
    SFD(self).mBufLen += len;
    SFD(self).mBuf[SFD(self).mBufLen] = 0;

    return TRUE;
}

// TRUE: Success
// FALSE: signal interrupt. set RCODE_SIGNAL_INTERRUPT on rcode and err message
BOOL fd_writec(sObject* self, char c)
{
    if((SFD(self).mBufLen+1) >= SFD(self).mBufSize) {
        int new_size = (SFD(self).mBufSize + 1) * 1.8;
        SFD(self).mBuf = REALLOC(SFD(self).mBuf, new_size);
        SFD(self).mBufSize = new_size;
    }

    SFD(self).mBuf[SFD(self).mBufLen++] = c;
    SFD(self).mBuf[SFD(self).mBufLen] = 0;

    if(gPipesSigInt) {
        gPipesSigInt = FALSE;
        return FALSE;
    }

    return TRUE;
}

// TRUE: Success
// FALSE: signal interrupt. set RCODE_SIGNAL_INTERRUPT on rcode and err message
BOOL fd_flash(sObject* self, int fd)
{
    if(!bufsiz_write(fd, SFD(self).mBuf)) {
        return FALSE;
    }

    SFD(self).mBuf[0] = 0;
    SFD(self).mBufLen = 0;

    return TRUE;
}

sObject* job_new_from_gc(char* name, pid_t pgroup, struct termios tty)
{
    sObject* self = gc_get_free_object(T_JOB, FALSE);

    SJOB(self).mName = STRDUP(name);
    SJOB(self).mPGroup = pgroup;
    SJOB(self).mTty = tty;

    vector_add(gJobs, self);
}

void job_delete_gc(sObject* self)
{
    if(SJOB(self).mName) FREE(SJOB(self).mName);
}

static BOOL nextout_reader(sObject* nextout, int* pipeoutfds, sRunInfo* runinfo, char* program)
{
    //if(!gPipesSigUser) {
        char buf[BUFSIZ+1];
        while(1) {
            if(gPipesSigInt) {
                err_msg("signal interrupt1", runinfo->mSName, runinfo->mSLine, program);
                gPipesSigInt = FALSE;
                runinfo->mRCode = RCODE_SIGNAL_INTERRUPT;
                return FALSE;
            }
            int r = read(pipeoutfds[0], buf, BUFSIZ);
            if(r < 0) {
                if(errno == EINTR) {
                    if(!fd_write(nextout, buf)) {
                        err_msg("signal interrupt2", runinfo->mSName, runinfo->mSLine, program);
                        runinfo->mRCode = RCODE_SIGNAL_INTERRUPT;
                        return FALSE;
                    }
                    break;
                }
                else {
                    perror("read mpipe");
                    exit(1);
                }
            }
            buf[r] = 0;

            if(!fd_write(nextout, buf)) {
                err_msg("signal interrupt3", runinfo->mSName, runinfo->mSLine, program);
                runinfo->mRCode = RCODE_SIGNAL_INTERRUPT;
                return FALSE;
            }

            if(r == 0) {
                break;
            }
        }
    //}

    (void)close(pipeoutfds[0]);

    return TRUE;
}

static BOOL nextin_writer(pid_t pid, sObject* nextin, int* pipeinfds, int* pipeoutfds, int* pipeerrfds, sRunInfo* runinfo, char* program)
{
    int pid2 = fork();
    if(pid2 < 0) {
        perror("fork2");
        exit(1);
    }
    /// writer process ///
    if(pid2 == 0) {
        if(pipeoutfds[0] != -1) {
            close(pipeoutfds[0]);
            close(pipeoutfds[1]);
        }
        if(pipeerrfds[0] != -1) {
            close(pipeerrfds[0]);
            close(pipeerrfds[1]);
        }

        pid2 = getpid();

        if(gAppType != kATOptC) {
            (void)setpgid(pid2, pid);
        }
        (void)close(pipeinfds[0]);

        //if(!gPipesSigUser) {
            if(!bufsiz_write(pipeinfds[1], SFD(nextin).mBuf)) {
                if(errno != EPIPE ) {
                    perror("write memory pipe");
                    exit(1);
                }
            }
        //}
        (void)close(pipeinfds[1]);

        exit(0);
    }
    /// the parent process ///
    else {
        if(gAppType != kATOptC) {
            (void)setpgid(pid2, pid);
        }

        (void)close(pipeinfds[0]);
        (void)close(pipeinfds[1]);
    }
    if(gAppType == kATOptC) {
        while(1) {
            int status;
            pid2 = waitpid(pid2, &status, WUNTRACED);

            if(pid2 < 0 && errno == EINTR) {
            }
            else if(WIFSTOPPED(status)) {
                kill(pid2, SIGCONT);
            }
            else if(WIFSIGNALED(status)) {
                err_msg("signal interrupt4", runinfo->mSName, runinfo->mSLine, program);
                return FALSE;
            }
            else {
                break;
            }
        }
    }
    else {
        int status;
        pid2 = waitpid(pid2, &status, WUNTRACED);

        if(WIFSIGNALED(status) || WIFSTOPPED(status))
        {
            err_msg("signal interrupt5", runinfo->mSName, runinfo->mSLine, program);
            return FALSE;
        }
    }

    return TRUE;
}

static BOOL wait_child_program(pid_t pid, int nextout2, sRunInfo* runinfo, char* program)
{
    if(!runinfo->mLastProgram) {
        if(gAppType == kATOptC) {
            while(1) {
                int status;
                pid = waitpid(pid, &status, WUNTRACED);

                if(pid < 0 && errno == EINTR) {
                }
                else if(WIFSTOPPED(status)) {
                    kill(pid, SIGCONT);
                }
                else if(WIFSIGNALED(status)) {
                    err_msg("signal interrupt6", runinfo->mSName, runinfo->mSLine, program);
                    return FALSE;
                }
                else {
                    break;
                }
            }
        }
        else {
            int status;
            pid = waitpid(pid, &status, WUNTRACED);

            if(WIFSTOPPED(status)) {
                err_msg("signal interrupt7", runinfo->mSName, runinfo->mSLine, program);

                kill(pid, SIGKILL);
                pid = waitpid(pid, &status, WUNTRACED);
                if(tcsetpgrp(0, getpid()) < 0) {
                    perror("tcsetpgrp(pipes)");
                    exit(1);
                }

                return FALSE;
            }
            else if(WIFSIGNALED(status)) {  // a pipes external program which is not a last command can't be stopped by CTRL-Z
                err_msg("signal interrupt8", runinfo->mSName, runinfo->mSLine, program);

                if(tcsetpgrp(0, getpid()) < 0) {
                    perror("tcsetpgrp(pipes)");
                    exit(1);
                }
                return FALSE;
            }
            if(tcsetpgrp(0, getpid()) < 0) {
                perror("tcsetpgrp(pipes)");
                exit(1);
            }
        }
    }
    /// last program ///
    else {
        /// a background job ///
        if(runinfo->mStatment->mFlags & STATMENT_BACKGROUND)
        {
            struct termios tty;
            tcgetattr(STDIN_FILENO, &tty);

            sStatment* statment = runinfo->mStatment;
            char title[BUFSIZ];
            title[0] = 0;
            int i;
            for(i=0; i<statment->mCommandsNum; i++) {
                xstrncat(title, statment->mCommands[i].mArgsRuntime[0], BUFSIZ);
                xstrncat(title, " ", BUFSIZ);
            }
            sObject* job = JOB_NEW_GC(title, pid, tty);
        }

        /// a forground job ///
        else {
            sigchld_block(0);       // don't block sigchild
            int status = 0;

            if(gAppType == kATOptC) {
                while(1) {
                    pid_t pid2 = waitpid(pid, &status, WUNTRACED);
                    if(pid2 < 0 && errno == EINTR) {
                    }
                    else if(WIFSTOPPED(status)) {
                        kill(pid2, SIGCONT);
                    }
                    else {
                        break;
                    }
                }
            }
            else {
                pid_t pid2 = waitpid(pid, &status, WUNTRACED);

                /// exited from signal ///
                if(WIFSIGNALED(status)) {
                    runinfo->mRCode = WTERMSIG(status) + 128;
                }
                /// exited normally ///
                else if(WIFEXITED(status)) {
                    runinfo->mRCode = WEXITSTATUS(status);
                }
                /// stopped from signal ///
                else if(WIFSTOPPED(status)) {
                    runinfo->mRCode = WSTOPSIG(status) + 128;

                    /// make a job ///
                    struct termios tty;
                    tcgetattr(STDIN_FILENO, &tty);

                    sStatment* statment = runinfo->mStatment;
                    char title[BUFSIZ];
                    title[0] = 0;
                    int i;
                    for(i=0; i<statment->mCommandsNum; i++) {
                        xstrncat(title, statment->mCommands[i].mArgsRuntime[0], BUFSIZ);
                        xstrncat(title, " ", BUFSIZ);
                    }
                    sObject* job = JOB_NEW_GC(title, pid, tty);
                }

                if(gAppType == kATConsoleApp) // && nextout2 == 1)
                {
                    if(tcsetpgrp(0, getpid()) < 0) {
                        perror("tcsetpgrp(pipes)");
                        exit(1);
                    }
                }
            }
        }
    }

    return TRUE;
}

static BOOL run_exec_cprog(sCommand* command, char* program, int nextin, int nextout, int nexterr)
{
    /// pipe ///
    if(nextin != 0) {
        if(dup2(nextin, 0) < 0) {
            char buf[32];
            snprintf(buf, 32, "dup2 3 nextin %d", nextin);
            perror(buf);
            exit(1);
        }
        if(close(nextin) < 0) { return FALSE; }
    }
    
    if(nextout != 1) {
        if(dup2(nextout, 1) < 0) {
            char buf[128];
            snprintf(buf, 128, "dup2 nextout (%d)", nextout);
            perror(buf);
            exit(1);
        }
        
        if(close(nextout) < 0) { return FALSE; }
    }
    
    if(nexterr != 2) {
        if(dup2(nexterr, 2) < 0) {
            perror("dup2 5");
            exit(1);
        }
        
        if(close(nexterr) < 0) { return FALSE; }
    }

    /// exec ///
    BOOL program_at_cd = TRUE;
    char* p = program;
    while(*p) {
        if(*p == '/') {
            program_at_cd = FALSE;
            break;
        }
        else {
            p++;
        }
    }

    if(program_at_cd) {
        if(access(program, X_OK) == 0) {
            fprintf(stderr, "This is program at current directory\n");
            kill(getppid(), SIGUSR1);
            exit(1);
        }
    }

    execv(program, command->mArgsRuntime);
    //fprintf(stderr, "exec('%s') error\n", command->mArgsRuntime[0]);  // comment out for try
    kill(getppid(), SIGUSR1);
    exit(1);
}

static BOOL run_external_command(char* program, sObject* nextin, sObject* nextout, sRunInfo* runinfo)
{
    int pipeinfds[2] = { -1, -1};
    int pipeoutfds[2] = { -1, -1 };
    int pipeerrfds[2] = { -1 , -1};
    int nextin2;
    int nextout2;
    int nexterr2;

    if(nextin == gStdin && SFD(gStdin).mBufLen == 0) {
        nextin2 = 0;
    }
    else {
        if(pipe(pipeinfds) < 0) {
            perror("pipe");
            exit(1);
        }
        nextin2 = pipeinfds[0];
    }
    if(nextout == gStdout) {
        nextout2 = 1;
        nexterr2 = 2;
    }
    else {
        if(pipe(pipeoutfds) < 0) {
            perror("pipe");
            exit(1);
        }
        nextout2 = pipeoutfds[1];

        if(pipe(pipeerrfds) < 0) {
            perror("pipe");
            exit(1);
        }
        nexterr2 = pipeerrfds[1];
    }

    /// fork ///
    pid_t pid = fork();
    if(pid < 0) {
        perror("fork");
        exit(1);
    }

    /// a child process ///
    if(pid == 0) {
        if(nextin2 != 0) { (void)close(pipeinfds[1]); }
        if(nextout2 != 1) { (void)close(pipeoutfds[0]); }
        if(nexterr2 != 2) {(void)close(pipeerrfds[0]); }

        pid = getpid();

        if(nextout2 == 1) {
            // a child process has a own process group
            if(gAppType != kATOptC) { (void)setpgid(pid, pid); }

            /// move the child program to forground
            if(gAppType == kATConsoleApp
                && !(runinfo->mStatment->mFlags & STATMENT_BACKGROUND))
            {
                if(tcsetpgrp(0, pid) < 0) {
                    perror("tcsetpgrp(child)");
                    exit(1);
                }
            }
        }
        else {
            if(gAppType != kATOptC) { (void)setpgid(pid, getpgid(getpid())); }
        }

        pipes_restore_signal_default();
        sigchld_block(0);

        if(!run_exec_cprog(runinfo->mCommand, program, nextin2, nextout2, nexterr2))
        {
            return FALSE;
        }

        return TRUE;
    }
    /// the parent process 
    else {
        pid_t pgroup;
        if(nextout2 == 1) {
            pgroup = pid; // a child process has a own process group
        }
        else {
            pgroup = getpgid(getpid());
        }

        if(gAppType != kATOptC) {
            (void)setpgid(pid, pgroup);
        }

        /// If a child process gets big data in pipes, it makes the pipes blocked.
        /// Therefore pipes can't waitpid and deadlock.
        /// Avoid this, pipes is ready for a writer process to write buffer to the pipes.
        if(nextin2 != 0) {
            if(!nextin_writer(pgroup, nextin, pipeinfds, pipeoutfds, pipeerrfds, runinfo, program)) {
                (void)wait_child_program(pid, nextout2, runinfo, program);
                return FALSE;
            }
        }
        if(nextout2 != 1) {
            (void)close(pipeoutfds[1]);
            if(!nextout_reader(nextout, pipeoutfds, runinfo, program)) {
                // wait everytime
                (void)wait_child_program(pid, nextout2, runinfo, program);
                return FALSE;
            }
        }
        if(nexterr2 != 2) {
            (void)close(pipeerrfds[1]);
            if(!nextout_reader(gStderr, pipeerrfds, runinfo, program)) {
                (void)wait_child_program(pid, nextout2, runinfo, program);
                return FALSE;
            }
        }

        // wait everytime
        if(!wait_child_program(pid, nextout2, runinfo, program)) {
            return FALSE;
        }

        return TRUE;
    }
}

BOOL run_function(sObject* fun, sObject* nextin, sObject* nextout, sRunInfo* runinfo, char** argv, int argc, sObject** blocks, int blocks_num)
{
    sObject* local_objects = SFUN(fun).mLocalObjects;
    sRunInfo* runinfo2 = SFUN(fun).mRunInfo;
    SFUN(fun).mRunInfo = runinfo;

    sObject* stackframe = UOBJECT_NEW_GC(8, gPipesObject, "_stackframe", FALSE);
    vector_add(gStackFrames, stackframe);
    //uobject_init(stackframe);
    SFUN(fun).mLocalObjects = stackframe;

    sObject* argv2 = VECTOR_NEW_GC(16, FALSE);
    int i;
    for(i=0; i<argc; i++) {
        vector_add(argv2, STRING_NEW_GC(argv[i], FALSE));
    }
    hash_put(SFUN(fun).mLocalObjects, "ARGV", argv2);

    sCommand* command = runinfo->mCommand;
    sObject* options = HASH_NEW_GC(16, FALSE);
    for(i=0; i<PIPES_OPTION_MAX; i++) {
        if(command->mOptions[i].mKey) {
            if(command->mOptions[i].mArg) {
                hash_put(options, SFUN(fun).mOptions[i].mKey, STRING_NEW_GC(command->mOptions[i].mArg, FALSE));
            }
            else {
                hash_put(options, command->mOptions[i].mKey, STRING_NEW_GC(command->mOptions[i].mKey, FALSE));
            }
        }
    }
    hash_put(SFUN(fun).mLocalObjects, "OPTIONS", options);

    sObject* arg_blocks = SFUN(fun).mArgBlocks;
    SFUN(fun).mArgBlocks = VECTOR_NEW_GC(16, FALSE);

    for(i=0; i<blocks_num; i++) {
        sObject* block = blocks[i];
        vector_add(SFUN(fun).mArgBlocks, block);
        //vector_add(SFUN(fun).mArgBlocks, block_clone_stack(block, T_BLOCK));
    }

    int rcode = 0;
    if(!run(fun, nextin, nextout, &rcode, runinfo->mCurrentObject, fun)) {
        if(rcode != RCODE_RETURN) {
            char buf[BUFSIZ];
            err_msg_adding("run time error", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
            runinfo->mRCode = rcode;
            SFUN(fun).mLocalObjects = local_objects;
            (void)vector_pop_back(gStackFrames);
            return FALSE;
        }
    }

runinfo->mRCode = rcode;

    (void)vector_pop_back(gStackFrames);

    SFUN(fun).mLocalObjects = local_objects;
    SFUN(fun).mArgBlocks = arg_blocks;
    SFUN(fun).mRunInfo = runinfo2;

    return TRUE;
}

static BOOL run_block(sObject* block, sObject* nextin, sObject* nextout, sRunInfo* runinfo)
{
    int rcode = 0;
    if(!run(block, nextin, nextout, &rcode, runinfo->mCurrentObject, runinfo->mRunningObject)) {
        sCommand* command = runinfo->mCommand;
        err_msg_adding("run time error", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
        runinfo->mRCode = rcode;
        return FALSE;
    }

    return TRUE;
}

static BOOL run_object(sObject* object, sObject* nextin, sObject* nextout, sRunInfo* runinfo)
{
    switch(TYPE(object)) {
        case T_NFUN: 
            if(gAppType == kATConsoleApp)
            {
                if(tcsetpgrp(0, getpid()) < 0) {
                    perror("tcsetpgrp inner command");
                    exit(1);
                }
            }

            runinfo->mRCode = RCODE_NFUN_INVALID_USSING;

            if(!SNFUN(object).mNativeFun(nextin, nextout, runinfo)) {
                return FALSE;
            }
            if(runinfo->mRCode == RCODE_NFUN_INVALID_USSING) {
                sCommand* command = runinfo->mCommand;
                err_msg("invalid command using", runinfo->mSName, runinfo->mSLine, command->mArgsRuntime[0]);
                return FALSE;
            }
            break;

        case T_FUN: {
            sCommand* command = runinfo->mCommand;
            runinfo->mCurrentObject = runinfo->mRecieverObject;
            if(!run_function(object, nextin, nextout, runinfo, command->mArgsRuntime + 1, command->mArgsNumRuntime -1, command->mBlocks, command->mBlocksNum)){
                return FALSE;
            }
            }
            break;

        case T_CLASS: {
            sCommand* command = runinfo->mCommand;
            if(!run_function(object, nextin, nextout, runinfo, command->mArgsRuntime + 1, command->mArgsNumRuntime -1, command->mBlocks, command->mBlocksNum)){
                return FALSE;
            }
            }
            break;

        case T_EXTERNAL:
            if(!run_external_command(SEXTERNAL(object).mPath, nextin, nextout, runinfo)) {
                return FALSE;
            }
            break;

        case T_BLOCK:
            if(!run_block(object, nextin, nextout, runinfo)) {
                return FALSE;
            }
            break;

        case T_STRING: {
            if(!fd_write(nextout, string_c_str(object))) {
                sCommand* command = runinfo->mCommand;
                err_msg("signal interrupt9", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
                runinfo->mRCode = RCODE_SIGNAL_INTERRUPT;
                return FALSE;
            }
            if(!fd_write(nextout, "\n")) {
                sCommand* command = runinfo->mCommand;
                err_msg("signal interrupt10", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
                runinfo->mRCode = RCODE_SIGNAL_INTERRUPT;
                return FALSE;
            }
            }
            break;

        case T_UOBJECT: {
            sObject* main_obj = hash_item(object, "main");
            runinfo->mRecieverObject = object;

            if(main_obj) {
                if(!run_object(main_obj, nextin, nextout, runinfo)) {
                    return FALSE;
                }
            }
            else {
                if(!cmd_mshow(nextin, nextout, runinfo)) {
                    return FALSE;
                }
            }
            }
            break;

        case T_VECTOR: {
            int j;
            for(j=0; j<vector_size(object); j++) {
                sObject* item = vector_item(object, j);

                if(!run_object(item, nextin, nextout, runinfo)) {
                    return FALSE;
                }
            }
            }
            break;

        case T_HASH: { 
            hash_it* it = hash_loop_begin(object);
            while(it) {
                char* key = hash_loop_key(it);
                if(!fd_write(nextout, key)) {
                    sCommand* command = runinfo->mCommand;
                    err_msg("signal interrupt9", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
                    runinfo->mRCode = RCODE_SIGNAL_INTERRUPT;
                    return FALSE;
                }
                if(!fd_write(nextout, "\n")) {
                    sCommand* command = runinfo->mCommand;
                    err_msg("signal interrupt10", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
                    runinfo->mRCode = RCODE_SIGNAL_INTERRUPT;
                    return FALSE;
                }

                sObject* item = hash_loop_item(it);

                if(!run_object(item, nextin, nextout, runinfo)) {
                    return FALSE;
                }

                it = hash_loop_next(it);
            }
            }
            break;

        default: {
            char buf[BUFSIZ];
            sCommand* command = runinfo->mCommand;
            err_msg("can't run this object", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
            }
            return FALSE;
    }

    return TRUE;
}

static BOOL stdin_read_out()
{
    char buf[BUFSIZ+1];
    while(TRUE) {
        int size = read(0, buf, BUFSIZ);
        if(size < 0) {
            return FALSE;
        }
        buf[size] = 0;
        if(size == 0) {
            break;
        }
        else {
            if(!fd_write(gStdin, buf)) {
                return FALSE;
            }
        }
    }

    return TRUE;
}

static BOOL statment_tree(sStatment* statment, sObject* pipein, sObject* pipeout, sRunInfo* runinfo, sObject* current_object)
{
    sObject* nextin;
    if(statment->mFlags & STATMENT_CONTEXTPIPE) {
        if(pipein == gStdin && SFD(gStdin).mBufLen == 0) {
            if(!stdin_read_out()) {
                sCommand* command = runinfo->mCommand;
                err_msg("signal interrupt11", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
                runinfo->mRCode = RCODE_SIGNAL_INTERRUPT;
                return FALSE;
            }
        }

        nextin = pipein;
    }
    else if(statment->mFlags & STATMENT_GLOBALPIPEIN) {
        nextin = FD_NEW_STACK();
        if(!fd_write(nextin, SFD(gGlobalPipe).mBuf)) {
            sCommand* command = runinfo->mCommand;
            err_msg("signal interrupt12", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
            runinfo->mRCode = RCODE_SIGNAL_INTERRUPT;
            return FALSE;
        }
    }
    else {
        nextin = pipein;
    }

    int i;
    for(i=0; i<statment->mCommandsNum; i++) {
        sCommand* command = runinfo->mCommand = statment->mCommands + i;

        runinfo->mCurrentObject = current_object;
        const BOOL last_program = runinfo->mLastProgram = i == statment->mCommandsNum-1;
        runinfo->mFilter = i != 0 || (statment->mFlags & (STATMENT_CONTEXTPIPE|STATMENT_GLOBALPIPEIN));

        sObject* nextout;
        if(last_program) {
            if(statment->mFlags & STATMENT_GLOBALPIPEOUT) {
                fd_clear(gGlobalPipe);
                nextout = gGlobalPipe;
            }
            else {
                nextout = pipeout;
            }
        }
        else {
            nextout = FD_NEW_STACK();
        }

        if(command->mArgsNum > 0) {
            if(command->mArgsEnv[0]) {
                err_msg("a command name must be determined staticaly. Instead of this, use eval inner command.", runinfo->mSName, runinfo->mSLine, NULL);
                return FALSE;
            }

            /// message passing ///
            if(command->mMessagesNum > 0) {
                sObject* reciever = current_object;
                sObject* object = access_object(command->mMessages[0], &reciever, runinfo->mRunningObject);
                int i = 1;
                while(i < command->mMessagesNum) {
                    if(object && TYPE(object) == T_UOBJECT) {
                        reciever = object;
                        object = hash_item(object, command->mMessages[i++]);
                    }
                    else {
                        err_msg("invalid message passing", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
                        return FALSE;
                    }
                }

                if(object && TYPE(object) == T_UOBJECT) {
                    reciever = object;
                    object = hash_item(object, command->mArgs[0]);

                    /// expand env ///
                    if(!sCommand_expand_env(command, object, nextin, nextout, runinfo))
                    {
                        return FALSE;
                    }

                    if(object) {
                        runinfo->mRecieverObject = reciever;
                        if(!run_object(object, nextin, nextout, runinfo)) {
                            return FALSE;
                        }
                    }
                    else {
                        err_msg("there is not this object", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
                        return FALSE;
                    }
                }
                else {
                    err_msg("invalid message passing", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
                    return FALSE;
                }
            }
            /// normal function ///
            else {
                sObject* reciever = current_object;
                sObject* object = access_object(command->mArgs[0], &reciever, runinfo->mRunningObject);

                if(object) {
                    /// expand env ///
                    if(!sCommand_expand_env(command, object, nextin, nextout, runinfo))
                    {
                        return FALSE;
                    }

                    runinfo->mRecieverObject = reciever;
                    if(!run_object(object, nextin, nextout, runinfo)) {
                        return FALSE;
                    }
                }
                else {
                    /// expand env ///
                    if(!sCommand_expand_env(command, NULL, nextin, nextout, runinfo))
                    {
                        return FALSE;
                    }

                    if(!run_external_command(command->mArgsRuntime[0], nextin, nextout, runinfo)) {
                        return FALSE;
                    }
                }
            }
            sCommand_sweep_runtime_info(command);
        }

        nextin = nextout;
    }

    /// reverse the return code ///
    if(statment->mFlags & STATMENT_REVERSE) {
        runinfo->mRCode = !runinfo->mRCode;
    }

    return TRUE;
}

BOOL run(sObject* block, sObject* pipein, sObject* pipeout, int* rcode, sObject* current_object, sObject* running_object)
{
    static int nest_level = 0;
    nest_level++;
    stack_start_stack();

    sRunInfo runinfo;
    memset(&runinfo, 0, sizeof(sRunInfo));

    runinfo.mRunningObject = running_object;

    int i;
    for(i=0; i<SBLOCK(block).mStatmentsNum; i++) {
        sStatment* statment = SBLOCK(block).mStatments + i;

        runinfo.mSName = statment->mFName;
        runinfo.mSLine = statment->mLine;
        runinfo.mRCode = 0;
        runinfo.mStatment = statment;
        runinfo.mNestLevel = nest_level;

        if(!statment_tree(statment, pipein, pipeout, &runinfo, current_object)) {
            fd_clear(gStdout);
            fd_clear(gStderr);

            *rcode = runinfo.mRCode;

            stack_end_stack();
            nest_level--;

            return FALSE;
        }

        /// flash stdout and stderr ///
        if(pipeout == gStdout) {
            if(!fd_flash(gStdout, 1)) {
                sCommand* command = runinfo.mCommand;
                err_msg("signal interrupt13", runinfo.mSName, runinfo.mSLine, command->mArgs[0]);
                *rcode = RCODE_SIGNAL_INTERRUPT;
                stack_end_stack();
                nest_level--;
                return FALSE;
            }
        }

        if(nest_level == 1) {
            if(!fd_flash(gStderr, 2)) {
                sCommand* command = runinfo.mCommand;
                err_msg("signal interrupt14", runinfo.mSName, runinfo.mSLine, command->mArgs[0]);
                *rcode = RCODE_SIGNAL_INTERRUPT;
                stack_end_stack();
                nest_level--;
                return FALSE;
            }
        }

        /// the end of statment ///
        if(gPipesSigUser) {
            gPipesSigUser = FALSE;
            sCommand* command = runinfo.mCommand;
            err_msg("command not found", runinfo.mSName, runinfo.mSLine, ""); //command->mArgs[0]);
            stack_end_stack();
            nest_level--;
            return FALSE;
        }
        else if(runinfo.mRCode == 128+SIGINT || gPipesSigInt) {
            gPipesSigInt = FALSE;
            sCommand* command = runinfo.mCommand;
            err_msg("signal interrupt15", runinfo.mSName, runinfo.mSLine, command->mArgs[0]);
            *rcode = RCODE_SIGNAL_INTERRUPT;
            stack_end_stack();
            nest_level--;
            return FALSE;
        }
        else if((statment->mFlags & STATMENT_OROR) && runinfo.mRCode == 0) {
            while(i<SBLOCK(block).mStatmentsNum
                    && !(statment->mFlags & STATMENT_NORMAL))
            {
                i++;
                statment = SBLOCK(block).mStatments + i;
            }
        }
        else if((statment->mFlags & STATMENT_ANDAND) && runinfo.mRCode != 0) {
            while(i<SBLOCK(block).mStatmentsNum
                    && !(statment->mFlags & STATMENT_NORMAL))
            {
                i++;
                statment = SBLOCK(block).mStatments + i;
            }
        }
    }

    *rcode = runinfo.mRCode;

    stack_end_stack();
    nest_level--;

static unsigned int n = 0;
if(!(n++ % 32)) (void)gc_sweep();

    return TRUE;
}

BOOL forground_job(int num)
{
    if(num>=0 && num < vector_size(gJobs)) {
        sObject* job = vector_item(gJobs, num);

        tcsetattr(STDIN_FILENO, TCSANOW, &SJOB(job).mTty);
        if(tcsetpgrp(0, SJOB(job).mPGroup) < 0) {
            perror("tcsetpgrp(fg)");
            return FALSE;
        }
        
        if(kill(SJOB(job).mPGroup, SIGCONT) < 0) {
            perror("kill(fg)");
            return FALSE;
        }

        /// wait ///
        int status = 0;
        pid_t pid = waitpid(SJOB(job).mPGroup, &status, WUNTRACED);

        if(pid < 0) {
            perror("waitpid(run_wait_cprogs)");
            return FALSE;
        }

        /// exited normally ///
        if(WIFEXITED(status)) {
            if(pipes_job_done) pipes_job_done(num, SJOB(job).mName);

            vector_erase(gJobs, num);

            /// forground pipes ///
            mreset_tty();
            if(tcsetpgrp(0, getpid()) < 0) {
                perror("tcsetpgrp");
                return FALSE;
            }
        }
        /// exited from signal ///
        else if(WIFSIGNALED(status)) {
            if(pipes_job_done) pipes_job_done(num, SJOB(job).mName);

            vector_erase(gJobs, num);

            /// froground pipes ///
            if(tcsetpgrp(0, getpid()) < 0) {
                perror("tcsetpgrp");
                return FALSE;
            }
        }
        /// stopped from signal ///
        else if(WIFSTOPPED(status)) {
            tcgetattr(STDIN_FILENO, &SJOB(job).mTty);

            /// forground pipes ///
            if(tcsetpgrp(0, getpid()) < 0) {
                perror("tcsetpgrp");
                return FALSE;
            }
        }

        return TRUE;
    }

    err_msg("invalid job number", "fg", 1, "fg");
    return FALSE;
}

BOOL background_job(int num)
{
    if(num>=0 && num < vector_size(gJobs)) {
        sObject* job = vector_item(gJobs, num);
        
        if(kill(SJOB(job).mPGroup, SIGCONT) < 0) {
            perror("kill(bg)");
            return FALSE;
        }
    }

    return TRUE;
}

void pipes_wait_background_job()
{
    int status;
    pid_t pid = waitpid(-1, &status, WNOHANG);

    if(pid >= 0) {
        int i;
        for(i=0; i<vector_size(gJobs); i++) {
            sObject* job = vector_item(gJobs, i);

            if(pid == SJOB(job).mPGroup) {
                if(pipes_job_done) pipes_job_done(i, SJOB(job).mName);

                vector_erase(gJobs, i);
                break;
            }
        }
    }
}

void pipes_kill_all_jobs()
{
    int i;
    for(i=0; i<vector_size(gJobs); i++) {
        sObject* job = vector_item(gJobs, i);

        kill(SJOB(job).mPGroup, SIGKILL);
    }

    vector_clear(gJobs);
}

// TRUE: Success. result is malloced.
// FALSE: signal interrupt. set RCODE_SIGNAL_INTERRUPT on rcode and err message. result is not malloced.
BOOL bufsiz_read(int fd, ALLOC char** result, int* result_len)
{
    int source_size = BUFSIZ * 8;
    int source_len = 0;
    char* source = MALLOC(BUFSIZ*8);
    source[0] = 0;

    char buf[BUFSIZ+1];
    while(TRUE) {
        int size = read(fd, buf, BUFSIZ);
        if(size < 0) {
            FREE(source);
            return FALSE;
        }
        buf[size] = 0;
        if(size < BUFSIZ) {
            if(source_len + size + 1 >= source_size) {
                source_size *= 2;
                source = REALLOC(source, source_size);
            }
            strcat(source, buf);
            break;
        }
        else {
            if(source_len + size + 1 >= source_size) {
                source_size *= 2;
                source = REALLOC(source, source_size);
            }
            strcat(source, buf);
            source_len += size;
        }
    }

    *result = source;
    *result_len = source_len;
    
    return TRUE;
}

BOOL load_file(char* fname, sObject* nextin, sObject* nextout, sRunInfo* runinfo, char** argv, int argc)
{
    int fd = open(fname, O_RDONLY);
    if(fd < 0) {
        err_msg("open error", runinfo->mSName, runinfo->mSLine, fname);
        return FALSE;
    }

    char* buf;
    int buf_size;
    if(!bufsiz_read(fd, ALLOC &buf, &buf_size)) {
        sCommand* command = runinfo->mCommand;
        err_msg("signal interrupt16", runinfo->mSName, runinfo->mSLine, command->mArgs[0]);
        runinfo->mRCode = RCODE_SIGNAL_INTERRUPT;
        FREE(buf);
        close(fd);
        return FALSE;
    }
    (void)close(fd);

    stack_start_stack();
    int rcode = 0;
    sObject* block = BLOCK_NEW_STACK();
    int sline = 1;
    if(parse(buf, fname, &sline, block, NULL, TRUE)) {
        FREE(buf);

        sObject* fun = FUN_NEW_STACK(NULL);
        sObject* stackframe = UOBJECT_NEW_GC(8, gPipesObject, "_stackframe", FALSE);
        vector_add(gStackFrames, stackframe);
        //uobject_init(stackframe);
        SFUN(fun).mLocalObjects = stackframe;

        sObject* argv2 = VECTOR_NEW_GC(16, FALSE);
        int i;
        for(i=0; i<argc; i++) {
            vector_add(argv2, STRING_NEW_GC(argv[i], FALSE));
        }
        hash_put(SFUN(fun).mLocalObjects, "ARGV", argv2);

        if(!run(block, nextin, nextout, &rcode, runinfo->mCurrentObject, fun))
        {
            if(rcode == RCODE_EXIT) {
            }
            else {
                stack_end_stack();
                (void)vector_pop_back(gStackFrames);
                return FALSE;
            }
        }
    }
    else {
        FREE(buf);
        sCommand* command = runinfo->mCommand;
        err_msg_adding("parser error", runinfo->mSName, runinfo->mSLine, "parser");
        stack_end_stack();
        (void)vector_pop_back(gStackFrames);
        return FALSE;
    }

    (void)vector_pop_back(gStackFrames);
    stack_end_stack();

    return TRUE;
}
